﻿using System;
using System.Diagnostics;
using System.Xml.Linq;
using System.Globalization;
using System.Collections.Generic;

namespace Microsoft.Office.OneNote
{
    [DebuggerDisplay("Page {Name}")]
    [OneNoteXmlElementAttribute("Page")]
    public sealed class OneNotePage : OneNoteHierarchyObject, IOneNoteObjectWithChildren
    {
        internal static readonly OneNoteProperty NameProperty = OneNoteProperty.Register(
            "name", typeof(String), typeof(OneNotePage));

        internal static readonly OneNoteProperty DateTimeProperty = OneNoteProperty.Register(
            "dateTime", typeof(DateTime), typeof(OneNotePage));

        internal static readonly OneNoteProperty LanguageProperty =
            RegisterContentPropertyNoNew<CultureInfo>("lang");

        internal static readonly OneNoteProperty StyleProperty =
            RegisterContentPropertyNoNew<String>("style");

        internal static readonly OneNoteProperty PageSettingsProperty = 
            RegisterContentProperty<OneNotePageSettings>("PageSettings");

        internal static readonly OneNoteProperty TitleProperty =
            RegisterContentProperty<OneNotePageTitle>("Title");

        private static OneNoteProperty RegisterContentPropertyNoNew<T>(string name) where T : class
        {
            return OneNoteProperty.Register(name, typeof(T), typeof(OneNotePage),
                OneNotePropertyFlags.RequiresGetValueCallback);
        }

        private static OneNoteProperty RegisterContentProperty<T>(string name) where T : class, new()
        {
            return OneNoteProperty.Register(name, typeof(T), typeof(OneNotePage),
                DefaultValueProvider.NewInstance<T>(),
                OneNotePropertyFlags.RequiresGetValueCallback);
        }


        private readonly OneNotePageObjectStorage _storage;
        private readonly OneNoteObjectCollection<OneNoteOutline> _outlines;

        internal OneNotePage(OneNoteObjectId id)
            : base(id)
        {
            _storage = new OneNotePageObjectStorage();
            _outlines = new OneNoteObjectCollection<OneNoteOutline>(_storage);

            SetInnerObjects(_storage);
        }

        public String Name
        {
            get { return GetValue<String>(NameProperty); }
            set { SetValue(NameProperty, value); }
        }

        public DateTime DateTime
        {
            get { return GetValue<DateTime>(DateTimeProperty); }
            set { SetValue(DateTimeProperty, value); }
        }

        public CultureInfo Language
        {
            get { return GetValue<CultureInfo>(LanguageProperty); }
            set { SetValue(LanguageProperty, value); }
        }

        public String Style
        {
            get { return GetValue<String>(StyleProperty); }
            set { SetValue(StyleProperty, value); }
        }

        public OneNoteObjectCollection<OneNoteOutline> Outlines
        {
            get
            {
                EnsureContentLoaded();
                return _outlines;
            }
        }

        public OneNotePageSettings PageSettings
        {
            get { return GetValue<OneNotePageSettings>(PageSettingsProperty); }
        }

        public OneNotePageTitle Title
        {
            get { return GetValue<OneNotePageTitle>(TitleProperty); }
        }

        internal override void OnPropertyRequested(OneNoteProperty property)
        {
            EnsureContentLoaded();
        }

        internal override void DoSubmitChanges(XDocument changes)
        {
            OneNoteApplication.Current.UpdatePageContent(changes, LastModifiedTime);
        }


        private void EnsureContentLoaded()
        {
            if (!ContentLoaded && !IsDeserializing)
            {
                XDocument content = OneNoteApplication.Current.GetPageContent(ObjectId);
                OneNoteSchema.Deserialize(this, content.Root);

                ContentLoaded = true;
            }
        }

        private bool ContentLoaded
        {
            get { return CheckFlagsAnd(OneNoteObjectFlags.ContentLoaded); }
            set { SetFlags(value, OneNoteObjectFlags.ContentLoaded); }
        }

        void IOneNoteObjectWithChildren.UpdateFrom(IEnumerable<XElement> elements)
        {
            _storage.UpdateFrom(elements);
        }
    }
}
